/*
@hehung
2023-2-8
转载请注明出处，版权由@hehung所有
email: 1398660197@qq.com
wechat: hehung95
*/

/*
AT命令程序是在瑞萨官方提供的示例代码的基础上二次开发优化而来。主要优化方向：
1. 减少了代码量，将之前每个AT命令的代码合并为了一个；
2. 优化了超时等待逻辑，如果收到目标命令会立刻退出，不会再等待到了设定直接长度在退出，AT命令响应时间加快；
3. 增加了AT命令的接收逻辑，可以在发送AT命令的情况下等待AT命令
*/
#include "hal_data.h"
#include "da16200_AT.h"
#include "app_common.h"
#include "app_led.h"
#include "app_wifi.h"



// this structure is used to cache the received AT informations
typedef struct
{
    char at_buf[DA16200_STR_LEN_256];
    uint16_t at_len;
    uint16_t at_timeout;
} s_AtCmdBfType;


/** AT Command sets */
/*LDRA_INSPECTED 27 D This structure must be accessible in user code. It cannot be static. */
da16200_at_cmd_set_t g_da16200_cmd_set[] =
{
    /** Intial AT command */
    [DA16200_AT_CMD_INDEX_ATZ] =
    {
        .p_cmd = (uint8_t *) "ATZ\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_10,
        .retry_delay = DA16200_DELAY_500MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /** Echo on/ off */
    [DA16200_AT_CMD_INDEX_ATE] =
    {
        .p_cmd = (uint8_t *) "ATE\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* ATF */
    [ DA16200_AT_CMD_INDEX_AT_ATF] =
    {
        .p_cmd = (uint8_t *) "ATF\r\n",
        .p_success_resp = (uint8_t *) "DONE",
        .max_resp_length = DA16200_STR_LEN_128,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_500MS,
        .wait_timeout = DA16200_DELAY_3000MS
    },
    /* Set boot mode */
    [DA16200_AT_CMD_INDEX_AT_TMRFNOINIT] =
    {
        .p_cmd = (uint8_t *) "AT+TMRFNOINIT=0\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* Set Station mode */
    [DA16200_AT_CMD_INDEX_AT_WFMODE] =
    {
        .p_cmd = (uint8_t *) "AT+WFMODE=0\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* Restart */
    [ DA16200_AT_CMD_INDEX_AT_RESTART] =
    {
        .p_cmd = (uint8_t *) "AT+RESTART\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_128,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_1000MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* 连接wifi热点 */
    [DA16200_AT_CMD_INDEX_AT_WFJAP] =
    {
        .p_cmd = (uint8_t *) "AT+WFJAP=hehungphone,4,1,1234543210\r\n",
        .p_success_resp = (uint8_t *) "+WFJAP:1",
        .max_resp_length = DA16200_STR_LEN_128,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_1000MS,
        .wait_timeout = DA16200_DELAY_5000MS
    },
    /* TCP连接 - Contact to the ONENET TCP server:183.230.40.40:1811 */
    [DA16200_AT_CMD_INDEX_AT_TRTC] =
    {
        .p_cmd = (uint8_t *) "AT+TRTC=183.230.40.40,1811,0\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_2000MS
    },
    // 断开TCP连接
    [DA16200_AT_CMD_INDEX_AT_TRTRM] =
    {
        .p_cmd = (uint8_t *) "AT+TRTRM=1\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* Set Country Code */
    [DA16200_AT_CMD_INDEX_AT_WFCC] =
    {
        .p_cmd = (uint8_t *) "AT+WFCC=CH\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* Set Country Code */
    [ DA16200_AT_CMD_INDEX_AT_WFSAP] =
    {
        .p_cmd = (uint8_t *) "AT+WFSAP=Renesas_Wifi,3,1,12345678,1,CH\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_128,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* Set IP */
    [ DA16200_AT_CMD_INDEX_AT_NWIP] =
    {
        .p_cmd = (uint8_t *) "AT+NWIP=1,192.168.10.2,255.255.255.0,192.168.10.1\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_1000MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* Start DHCP */
    [ DA16200_AT_CMD_INDEX_AT_NWDHS] =
    {
        .p_cmd = (uint8_t *) "AT+NWDHS=1\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* DHCP IP area set*/
    [ DA16200_AT_CMD_INDEX_AT_NWDHR] =
    {
        .p_cmd = (uint8_t *) "AT+NWDHR=192.168.10.3,192.168.10.10\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* TCP server port set*/
    [ DA16200_AT_CMD_INDEX_AT_TRTS] =
    {
        .p_cmd = (uint8_t *) "AT+TRTS=80\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* TCP server port message save */
    [ DA16200_AT_CMD_INDEX_AT_TRSAVE] =
    {
        .p_cmd = (uint8_t *) "AT+TRSAVE\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* Wifi AP connect status */
    [ DA16200_AT_CMD_INDEX_AT_WFSTA] =
    {
        .p_cmd = (uint8_t *) "AT+WFSTA\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_1,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    /* sntp start with address 'pool.ntp.org'*/
    [ DA16200_AT_CMD_INDEX_AT_NWSNTP_START] =
    {
        .p_cmd = (uint8_t *) "AT+NWSNTP=1,pool.ntp.org,60\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    [ DA16200_AT_CMD_INDEX_AT_NWSNTP_STOP] =
    {
        .p_cmd = (uint8_t *) "AT+NWSNTP=0\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    [ DA16200_AT_CMD_INDEX_AT_NWSNTP_STATUS] =
    {
        .p_cmd = (uint8_t *) "AT+NWSNTP=?\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_200MS,
        .wait_timeout = DA16200_DELAY_1000MS
    },
    [ DA16200_AT_CMD_INDEX_AT_TZONE_GET] =
    {
        .p_cmd = (uint8_t *) "AT+TZONE=?\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_100MS,
        .wait_timeout = DA16200_DELAY_500MS
    },
    [ DA16200_AT_CMD_INDEX_AT_TZONE_SET] =
    {
        .p_cmd = (uint8_t *) "AT+TZONE=28800\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_100MS,
        .wait_timeout = DA16200_DELAY_500MS
    },
    [ DA16200_AT_CMD_INDEX_AT_TIME_GET] =
    {
        .p_cmd = (uint8_t *) "AT+TIME=?\r\n",
        .p_success_resp = (uint8_t *) "+TIME",
        .max_resp_length = DA16200_STR_LEN_64,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_100MS,
        .wait_timeout = DA16200_DELAY_500MS
    },
    [DA16200_AT_CMD_INDEX_AT_NWDHC] = 
    {
        .p_cmd = (uint8_t *) "AT+NWDHC\r\n",
        .p_success_resp = (uint8_t *) "OK",
        .max_resp_length = DA16200_STR_LEN_32,
        .retry = DA16200_RETRY_VALUE_5,
        .retry_delay = DA16200_DELAY_100MS,
        .wait_timeout = DA16200_DELAY_500MS        
    }
};

static uint32_t g_wifi_receive_time_count = 0;
static uint32_t count = 0;
static demo_data_t g_demo_data;
static s_AtCmdBfType g_at_buf;
  

static s_Da16200FbType RespList[] = 
{
    {Da16200_TRDTC_response, "+TRDTC:1"}
};


// DA16200 wifi module AT command
fsp_err_t Da1620_ATCommandExe(da16200_at_cmd_index_t at_cmd, 
                                uint8_t *resp_buff)
{
    uint16_t bytes_read = 0U;
    uint16_t bytes_write;
    fsp_err_t result = FSP_ERR_ASSERTION;
    uint8_t retry_count = 0U;
    da16200_at_cmd_set_t  * p_cmd_set = g_da16200_cmd_set;

    // setting mutex for lock wifi uart
    if (pdTRUE == xSemaphoreTake(wifi_mutex, portMAX_DELAY))
    {
        do
        {
            /** AT MODODR command **/
            bytes_write = (uint16_t) strlen((char *) p_cmd_set[at_cmd].p_cmd);
            wifi_serial_write((uint8_t*)p_cmd_set[at_cmd].p_cmd, bytes_write);

            bytes_read = p_cmd_set[at_cmd].max_resp_length;

            /** Clear respond memory **/
            memset (resp_buff, 0, bytes_read);

            result = wifi_serial_read(resp_buff, 
                                      &bytes_read, 
                                      (const char*)p_cmd_set[at_cmd].p_success_resp, 
                                      p_cmd_set[at_cmd].wait_timeout);

            if (FSP_SUCCESS == result)
            {
                break;
            }
        
            DELAY_MS(p_cmd_set[at_cmd].retry_delay);

            ++retry_count;
        }
        while(retry_count < p_cmd_set[at_cmd].retry);

        // release the mutex, unlock
        (void)xSemaphoreGive(wifi_mutex);
    }

    return result;
}

/************************************************************************************
* Name:       is_str_present
* Function:   compare string
* Parameters: p_resp_str, p_search_str
* Return:     comparing result
************************************************************************************/
uint8_t is_str_present(const char * p_resp_str, const char * p_search_str)
{
    if (strstr (p_resp_str, p_search_str))
    {
        return SF_WIFI_TRUE;
    }

    return SF_WIFI_FALSE;
}

/************************************************************************************
* Name:       wifi_serial_read
* Function:   read receive data
* Parameters: p_dest, p_bytes, p_resp_ptr, timeout_ms
* Return:     read result
************************************************************************************/
fsp_err_t wifi_serial_read(uint8_t * p_dest, 
                           const uint16_t * p_bytes, 
                           const char * p_resp_ptr, 
                           uint32_t timeout_ms)
{
    fsp_err_t status = FSP_SUCCESS;
    uint16_t bytes = *p_bytes;
    uint16_t i;
    uint8_t expected_resp_present;

    g_wifi_receive_time_count = 0U;

    do
    {
        // some data received from DA16200
        if (g_at_buf.at_len >= strlen(p_resp_ptr))
        {
            if (g_at_buf.at_len >= bytes)
            {
                /* Overflow */
                status = FSP_ERR_RXBUF_OVERFLOW;
                break;
            } 

            // compare the response data
            expected_resp_present = is_str_present(g_at_buf.at_buf, p_resp_ptr);
            if(SF_WIFI_TRUE == expected_resp_present)
            {
                printf ("==>da16200 recv:%s\n", g_at_buf.at_buf);
                status = FSP_SUCCESS;
                break;
            }
            // memset(g_at_buf.at_buf, '\0', sizeof(g_at_buf.at_buf));
        }

        expected_resp_present = is_str_present(g_at_buf.at_buf, "ERROR");
        if(SF_WIFI_TRUE == expected_resp_present)
        {
            // error
            printf ("==>da16200 error:%s\n", g_at_buf.at_buf);
            status = FSP_ERR_ASSERTION;
            break;
        }

        // timeout
        if(g_wifi_receive_time_count >= timeout_ms)
        {
            if (g_at_buf.at_len >= 0)
                memset(g_at_buf.at_buf, '\0', sizeof(g_at_buf.at_buf));
            g_wifi_receive_time_count = 0U;
            status = FSP_ERR_TIMEOUT;
            break;
        }

        /* Added delay for freeRTOS and used for solved thread dead */
        DELAY_MS(1);
    } while(1);

    // load the data to dest buffer
    if (FSP_SUCCESS == status)
    {
        for (i = 0; i < g_at_buf.at_len; i++)
        {
            *p_dest = g_at_buf.at_buf[i];
            p_dest ++;
        }
    }
    
    return status;
}


/************************************************************************************
* Name:       wifi_serial_write
* Function:   write data
* Parameters: p_src, bytes
* Return:     write result
************************************************************************************/
fsp_err_t wifi_serial_write(uint8_t * p_src, uint16_t bytes)
{
    fsp_err_t status = FSP_SUCCESS;

    if (g_at_buf.at_len > 0U)
    {
        g_at_buf.at_len = 0U;
        memset(g_at_buf.at_buf, '\0', sizeof(g_at_buf.at_buf));
    }
    status = g_uart0.p_api->write(g_uart0.p_ctrl, p_src, bytes);

    return(status);
}

/************************************************************************************
* Name:       uart0_notification
* Function:   write data callback
* Parameters: p_args
* Return:     write result
************************************************************************************/
void uart0_notification(uart_callback_args_t *p_args)
{
    if (p_args->event == UART_EVENT_RX_CHAR)
    {
        if (g_at_buf.at_len < (DA16200_STR_LEN_256 - 1U))
        {
            // save the data into the buffer 'g_at_buf.at_buf'
            g_at_buf.at_buf[g_at_buf.at_len] = (uint8_t)p_args->data;
            g_at_buf.at_len ++;
        }
    }
}

/************************************************************************************
* Name:       timer0_1ms_callback
* Function:   1ms callback
* Parameters: p_args
* Return:     1ms timing
************************************************************************************/
void timer0_1ms_callback(timer_callback_args_t *p_args)
{
    FSP_PARAMETER_NOT_USED(p_args);
    g_wifi_receive_time_count++;

    g_demo_data.led_blink_count++;
    if(g_demo_data.led_blink_count >= 1000)
    {
        count++;
        g_demo_data.led_blink_count = 0;
        if(count%2)
        {
            Led_Ctrl(LED_3, 1);
        }
        else
        {
            Led_Ctrl(LED_3, 0);
        }
    }

    // Used for handle the UART data from DA16200

    WIfi_CheckPreiodLoop();
}

// This function is used for handle the response data
void Da16200_FeedbackHandle(void)
{
    uint8_t i;

    for (i = 0; i < (sizeof(RespList) / sizeof(s_Da16200FbType)); i++)
    {
        if (SF_WIFI_TRUE == is_str_present(g_at_buf.at_buf, RespList->p_cmd))
        {
            RespList->RespFbFunc(g_at_buf.at_buf, g_at_buf.at_len);
        }
    }
    if (g_at_buf.at_len > 0)
    {
        ENTER_CRITICAL();
        // clear buffer
        memset(g_at_buf.at_buf, '\0', sizeof(g_at_buf.at_buf));
        g_at_buf.at_len = 0;
        EXIT_CRITICAL();
    }
}
